package com.g7.figmaapi.common.aop;

import org.apache.ibatis.javassist.*;
import org.apache.ibatis.javassist.bytecode.CodeAttribute;
import org.apache.ibatis.javassist.bytecode.LocalVariableAttribute;
import org.apache.ibatis.javassist.bytecode.MethodInfo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * AOP实现日志管理
 *
 * @author taoxiangqian
 * @since 2023/10/26
 **/
@Aspect
@Component
public class LoggerManageAop {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Pointcut("execution(* com.g7..*.controller.*Controller*.*(..))")
    public void webLog() {
    }

    /**
     * 排除打印查询方法日志，业务功能需要时，单独增加
     *
     * @author taoxiangqian
     * @since 2023/10/26
     **/
    @Pointcut("execution(* com.g7..*.controller.*Controller*.sel*(..))")
    public void webLogExcludeSel() {
    }

    /**
     * 控制层打印入参和出参日志
     *
     * @param point
     * @return java.lang.Object
     * @author taoxiangqian
     * @since 2023/10/26
     **/
    @Around("webLog() && !webLogExcludeSel()")
    public Object printMethodParams(ProceedingJoinPoint point) throws Throwable {
        ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attrs.getRequest();
        String uri = request.getRequestURI();
        //获取方法参数名称
        String[] paramNames = getFieldsName(point.getTarget().getClass().getName(), point.getSignature().getName());
        //打印方法的参数名和参数值
        Object[] args = point.getArgs();
        String params = logParam(paramNames, args);
        logger.info("当前请求路径" + uri + ";请求参数[" + params + "]");
        Object obj = point.proceed();
        logger.info("当前请求参数:[" + params + "];响应参数：[" + (obj == null ? null : obj.toString()) + "]");
        return obj;
    }

    /**
     * @param className
     * @param methodName
     * @return java.lang.String[]
     * @author taoxiangqian
     * @since 2023/10/26
     **/
    private String[] getFieldsName(String className, String methodName) throws Exception {
        Class<?> clazz = Class.forName(className);
        String clazzName = clazz.getName();
        ClassPool pool = ClassPool.getDefault();
        ClassClassPath classPath = new ClassClassPath(clazz);
        pool.insertClassPath(classPath);

        CtClass ctClass = pool.get(clazzName);
        CtMethod ctMethod = ctClass.getDeclaredMethod(methodName);
        MethodInfo methodInfo = ctMethod.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if (attr == null) {
            return null;
        }
        String[] paramsArgsName = new String[ctMethod.getParameterTypes().length];
        int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1;
        for (int i = 0; i < paramsArgsName.length; i++) {
            paramsArgsName[i] = attr.variableName(i + pos);
        }
        return paramsArgsName;
    }

    /**
     * 判断是否为基本类型：包括String
     *
     * @param clazz clazz
     * @return true：是;     false：不是
     */
    private boolean isPrimite(Class<?> clazz) {
        if (clazz.isPrimitive() || clazz == String.class) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 打印方法参数值  基本类型直接打印，非基本类型需要重写toString方法
     *
     * @param paramsArgsName  方法参数名数组
     * @param paramsArgsValue 方法参数值数组
     */
    private String logParam(String[] paramsArgsName, Object[] paramsArgsValue) {
        if (paramsArgsName == null || paramsArgsName.length == 0 || paramsArgsValue == null || paramsArgsValue.length == 0) {
            logger.debug("该方法没有参数");
            return "";
        }
        StringBuffer buffer = new StringBuffer();
        for (int i = 0; i < paramsArgsName.length; i++) {
            //参数名
            String name = paramsArgsName[i];
            //参数值
            Object value = paramsArgsValue[i];
            buffer.append(name + " = ");
            if (isPrimite(value.getClass())) {
                buffer.append(value + "  ,");
            } else {
                buffer.append(value.toString() + "  ,");
            }
        }
        return buffer.toString();
    }

}
